package com.amon.canary;

import android.os.Build;
import android.util.Log;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class NativeThreadMonitor {
    static {
        System.loadLibrary("canary");
        nativeInit(Build.VERSION.SDK_INT);
    }

    private static final HashMap<Integer, DeadLockThread> mDeadLocks = new HashMap<>();

    public static void handle() {
        Set<Thread> threadSet = NativeCrashMonitor.getAllThread();
        for (Thread thread : threadSet) {
            if (thread.getState() == Thread.State.BLOCKED) {
                long threadNativeAddress = (long) ReflectUtils.getFiledObject(thread, "nativePeer");
                if (threadNativeAddress == 0) {
                    continue;
                }
                int blockThreadId = NativeThreadMonitor.getContentThreadId(threadNativeAddress); //等待的那把锁，被持有的线程ID
                int currentThreadId = NativeThreadMonitor.getCurrentThreadId(threadNativeAddress, Build.VERSION.SDK_INT);
                Log.e("TAG", "blockThreadId -> " + blockThreadId + " , currentThreadId -> " + currentThreadId);
                mDeadLocks.put(currentThreadId, new DeadLockThread(currentThreadId, blockThreadId, thread));
            }
        }
        List<Map<Integer, Thread>> deadLockThreadGroup = deadLockThreadGroup();
        for (Map<Integer, Thread> group : deadLockThreadGroup) {
            for (Integer curId : group.keySet()) {
                DeadLockThread deadLockThread = mDeadLocks.get(curId);
                if (deadLockThread == null) {
                    continue;
                }
                Thread waitThread = group.get(deadLockThread.blockThreadId);
                if (waitThread == null) {
                    continue;
                }
                Thread deadThread = group.get(deadLockThread.currentThreadId);

                Log.e("TAG", "thread_name = " + deadThread.getName());
                Log.e("TAG", "wait_name = " + waitThread.getName());

                StackTraceElement[] stackTraceElements = deadThread.getStackTrace();
                for (StackTraceElement stackTraceElement : stackTraceElements) {
                    Log.e("TAG", stackTraceElement.toString());
                }
            }
        }
    }

    private static List<Map<Integer, Thread>> deadLockThreadGroup() {
        HashSet<Integer> traversalThreadIds = new HashSet<>();
        List<Map<Integer, Thread>> lockThreadGroup = new ArrayList<>();
        for (Integer currentThreadId : mDeadLocks.keySet()) {
            if (traversalThreadIds.contains(currentThreadId)) {
                continue;
            }
            Map<Integer, Thread> deadLockGroup = findDeadLockLink(currentThreadId, new HashMap<>());
            traversalThreadIds.addAll(deadLockGroup.keySet());
            lockThreadGroup.add(deadLockGroup);
        }
        return lockThreadGroup;
    }

    private static Map<Integer, Thread> findDeadLockLink(Integer currentThreadId, HashMap<Integer, Thread> group) {
        DeadLockThread deadLockThread = mDeadLocks.get(currentThreadId);
        if (currentThreadId == 0 || deadLockThread == null) {
            return new HashMap<>();
        }
        if (group.containsKey(currentThreadId)) {
            return group;
        }
        group.put(currentThreadId, deadLockThread.thread);
        return findDeadLockLink(deadLockThread.blockThreadId, group);
    }

    private static native int getCurrentThreadId(long threadNativeAddress, int sdkInt);

    private static native int getContentThreadId(long threadNativeAddress);

    public static native int nativeInit(int sdkVersion);

    public static native int monitorThread();
}
